package com.sxbbc.common.basics.utils.idcard;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import com.sxbbc.common.basics.utils.calculation.CalcuUtils;
import com.sxbbc.common.basics.utils.encrypts.pwd.PwdEncode;

/**
 * @author leehao
 *
 * 核验身份证号码是否有效并通过身份证号码获取信息
 * 1. 出生日期
 * 2. 年龄
 * 3. 籍贯(仅省份)
 * 4. 性别
 *
 * 身份证号码验证
 * a.号码的结构
 *  公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为：六位数字地址码,
 *  八位数字出生日期码,三位数字顺序码和一位数字校验码。
 * b.地址码(前六位数）
 *  表示编码对象常住户口所在县(市、旗、区)的行政区划代码,按GB/T2260的规定执行。
 * c.出生日期码（第七位至十四位）
 *  表示编码对象出生的年、月、日,按GB/T7408的规定执行,年、月、日代码之间不用分隔符。
 * d.顺序码（第十五位至十七位）
 *  表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
 * e.校验码（第十八位数）
 *  (e1)十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0,  , 16 ,先对前17位数字的权求和
 *      Ai:表示第i位置上的身份证号码数字值
 *      Wi:表示第i位置上的加权因子
 *      Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
 *  (e2)计算模 Y = mod(S, 11)
 *  (e3)通过模得到对应的校验码 Y: 0 1 2 3 4 5 6 7 8 9 10 校验码: 1 0 X 9 8 7 6 5 4 3 2
 */
public class IDCardUtil {

    private static IDCardUtil idCardUtil = null;

    public static IDCardUtil getInstance(){
        if (idCardUtil == null) {
            synchronized (IDCardUtil.class){
                if (idCardUtil == null) {
                    idCardUtil = new IDCardUtil();
                }
            }
        }
        return idCardUtil;
    }

    /**
     * 15位身份证号码校验
     */
    private static final Integer CARDNUMS_FIFTEEN = 15;

    /**
     * 18位身份证号码校验
     */
    private static final Integer CARDNUMS_EIGHTEEN = 18;

    /**
     * 身份证号码是否有效
     */
    private static boolean IS_VALID_CARDNUM = false;

    /**
     * 区域代码与名称
     */
    private final static Map<Integer, String> zoneNum = new HashMap<Integer, String>();
    static {
        zoneNum.put(11, "北京");
        zoneNum.put(12, "天津");
        zoneNum.put(13, "河北");
        zoneNum.put(14, "山西");
        zoneNum.put(15, "内蒙古");
        zoneNum.put(21, "辽宁");
        zoneNum.put(22, "吉林");
        zoneNum.put(23, "黑龙江");
        zoneNum.put(31, "上海");
        zoneNum.put(32, "江苏");
        zoneNum.put(33, "浙江");
        zoneNum.put(34, "安徽");
        zoneNum.put(35, "福建");
        zoneNum.put(36, "江西");
        zoneNum.put(37, "山东");
        zoneNum.put(41, "河南");
        zoneNum.put(42, "湖北");
        zoneNum.put(43, "湖南");
        zoneNum.put(44, "广东");
        zoneNum.put(45, "广西");
        zoneNum.put(46, "海南");
        zoneNum.put(50, "重庆");
        zoneNum.put(51, "四川");
        zoneNum.put(52, "贵州");
        zoneNum.put(53, "云南");
        zoneNum.put(54, "西藏");
        zoneNum.put(61, "陕西");
        zoneNum.put(62, "甘肃");
        zoneNum.put(63, "青海");
        zoneNum.put(64, "宁夏");
        zoneNum.put(65, "新疆");
        zoneNum.put(71, "台湾");
        zoneNum.put(81, "香港");
        zoneNum.put(82, "澳门");
        zoneNum.put(91, "国外");
    }

    private final static int[] PARITYBIT = { '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' };
    private final static int[] POWER_LIST = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };

    /**
     * 根据身份证的号码算出当前身份证持有者的性别和年龄 18位身份证
     * @return 信息map集合
     */
    public Map<String, Object> getCardInfo(String cardCode){
        Map<String, Object> map = new HashMap<String, Object>();
        // 判断身份证号码是否有效
        IS_VALID_CARDNUM = validIdCard(cardCode);
        if(!IS_VALID_CARDNUM){
            map.put("身份证号码无效",cardCode);
            return map;
        }
        // 得到年份
        String year = cardCode.substring(6).substring(0, 4);
        // 得到月份
        String month = cardCode.substring(10).substring(0, 2);
        //得到日
        String day=cardCode.substring(12).substring(0,2);
        String birthday = year+"-"+month+"-"+day;
        String province = zoneNum.get(Integer.valueOf(cardCode.substring(0, 2)));
        String sex;
        boolean temp = Integer.parseInt(cardCode.substring(16).substring(0, 1)) % 2 == 0;
        // 判断性别
        if (temp) {
            sex = "女";
        } else {
            sex = "男";
        }
        // 得到当前的系统时间
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        // 当前年份
        String nowYear = format.format(date).substring(0, 4);
        // 月份
        String nowMonth = format.format(date).substring(5, 7);
        // String fday=format.format(date).substring(8,10);
        int age = 0;
        // 当前月份大于用户出身的月份表示已过生
        if (Integer.parseInt(month) <= Integer.parseInt(nowMonth)) {
            age = Integer.parseInt(nowYear) - Integer.parseInt(year) + 1;
        // 当前用户还没过生
        } else {
            age = Integer.parseInt(nowYear) - Integer.parseInt(year);
        }
        map.put("身份证号码",cardCode);
        map.put("性别", sex);
        map.put("年龄", age);
        map.put("出生日期",birthday);
        map.put("籍贯",province);
        return map;
    }

    /**
     * 15位身份证的验证
     */
    public Map<String, Object> getCardInfo15W(String cardCode) {
        Map<String, Object> map = new HashMap<String, Object>();
        // 年份
        String uyear = "19" + cardCode.substring(6, 8);
        // 月份
        String umonth = cardCode.substring(8, 10);
        //日
         String uday=cardCode.substring(10, 12);
        String birthday = uyear+"-"+umonth+"-"+uday;
        // 用户的性别
        String usex = cardCode.substring(14, 15);
        String sex;
        if (Integer.parseInt(usex) % 2 == 0) {
            sex = "女";
        } else {
            sex = "男";
        }
        // 得到当前的系统时间
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        // 当前年份
        String nowYear = format.format(date).substring(0, 4);
        // 月份
        String nowMonth = format.format(date).substring(5, 7);
        // String fday=format.format(date).substring(8,10);
        int age = 0;
        // 当前月份大于用户出身的月份表示已过生
        if (Integer.parseInt(umonth) <= Integer.parseInt(nowMonth)) {
            age = Integer.parseInt(nowYear) - Integer.parseInt(uyear) + 1;
        } else {// 当前用户还没过生
            age = Integer.parseInt(nowYear) - Integer.parseInt(uyear);
        }
        map.put("身份证号码",cardCode);
        map.put("性别", sex);
        map.put("年龄", age);
        map.put("出生日期",birthday);
        return map;
    }

    /**
     * 身份证号码验证
     * @param cardCode 身份证号码
     * @return 身份证有效返回true,反之返回false
     */
    public boolean validIdCard(String cardCode) {
        if (cardCode == null || (cardCode.length() != CARDNUMS_FIFTEEN && cardCode.length() != CARDNUMS_EIGHTEEN)){
            return false;
        }
        final char[] cs = cardCode.toUpperCase().toCharArray();
        // (1)校验位数
        int power = 0;
        // 循环比正则表达式更快
        for (int i = 0; i < cs.length; i++) {
            if (i == cs.length - 1 && cs[i] == 'X'){
                // 最后一位可以是X或者x
                break;
            }
            if (cs[i] < '0' || cs[i] > '9'){
                return false;
            }
            if (i < cs.length - 1){
                power += (cs[i] - '0') * POWER_LIST[i];
            }
        }
        // (2)校验区位码
        if (!zoneNum.containsKey(Integer.valueOf(cardCode.substring(0, 2)))) {
            return false;
        }
        // (3)校验年份
        String year = cardCode.length() == 15 ? "19" + cardCode.substring(6, 8) : cardCode.substring(6, 10);
        final int iyear = Integer.parseInt(year);
        if (iyear < 1900 || iyear > Calendar.getInstance().get(Calendar.YEAR)) {
            // 1900年的PASS,超过今年的PASS
            return false;
        }
        // (4)校验月份
        String month = cardCode.length() == 15 ? cardCode.substring(8, 10) : cardCode.substring(10, 12);
        final int imonth = Integer.parseInt(month);
        if (imonth < 1 || imonth > 12){
            return false;
        }
        // (5)校验天数
        String day = cardCode.length() == 15 ? cardCode.substring(10, 12) : cardCode.substring(12, 14);
        final int iday = Integer.parseInt(day);
        if (iday < 1 || iday > 31){
            return false;
        }
        // (6)校验一个合法的年月日
        if (!validate(iyear, imonth, iday)){
            return false;
        }
        // (7)校验“校验码”
        if (cardCode.length() == 15){
            return true;
        }
        return cs[cs.length - 1] == PARITYBIT[power % 11];
    }

    private boolean validate(int year, int month, int day) {
        //比如考虑闰月,大小月等
        return true;
    }
    
    
    /** 台胞证位数 */
    public static final Integer CARDNUMS_TAIWAN = 10;
    
    /** 台胞证正则  */
    public static final String REGEX_TAIWAN = "^[a-zA-Z][0-9]{9}$";
    
    /** 通算值规则 */
    private final static int[] POWER_LIST_TAIWAN = { 9,8, 7, 6, 5, 4, 3, 2, 1 };
    
    /**
     * 
     * 台湾地区身份证验证
     * 通算值 = 首字母对应的第一位验证码+ 首字母对应的第二位验证码 * 9 + 性别码 * 8 + 第二位数字 * 7 + 
     * 第三位数字 * 6 + 第四位数字 * 5 + 第五位数字 * 4 + 第六位数字 * 3 + 第七位数字 * 2 + 第八位数字 * 1
     * 最后一位数 =10- 通算值的末尾数
     * @param cardCode
     * @return
     * @author Wahyee
     */
    public boolean vaildIdCardFormTaiwan(String cardCode) {
        if (cardCode == null || cardCode.length() != CARDNUMS_TAIWAN){
            return false;
        }
        // (1)正则匹配
        if(!cardCode.matches(REGEX_TAIWAN)) {
        	return false;
        }
        // (2)校验区位码
        if (ZoneNumTaiwanEnums.getId(cardCode.substring(0, 1)) == null) {
            return false;
        }
        // (3)校验性别
        if(!cardCode.substring(1, 2).equals("1") &&  !cardCode.substring(1, 2).equals("2")) {
        	return false;
        }
        // (4)校验验证码
        int a =  ZoneNumTaiwanEnums.getId(cardCode.substring(0, 1));
        String b = ""+a;
        final char[] cs = cardCode.toUpperCase().toCharArray();
        int power = 0;
        for (int i = 0; i < cs.length-1; i++) {
        	if(i == 0) {
        		power += Integer.valueOf(b.substring(0,1)) + Integer.valueOf(b.substring(1,2)) * POWER_LIST_TAIWAN[i];
        	}else {
            	power += ((cs[i] - '0') * POWER_LIST_TAIWAN[i]);
        	}
        }
        return  cs[cs.length - 1] - '0' == 10 - power % 10;
    }
}
